package cn.seaboot.admin.security.auth;

import cn.seaboot.admin.mvc.cache.CacheName;
import cn.seaboot.admin.security.bean.SecurityChain;
import cn.seaboot.admin.security.context.SecurityAntAuthorityCache;
import cn.seaboot.admin.security.service.SecurityChainService;
import cn.seaboot.admin.user.bean.Role;
import cn.seaboot.admin.user.bean.User;
import cn.seaboot.admin.user.bean.UserDept;
import cn.seaboot.admin.user.dao.UserDao;
import cn.seaboot.admin.user.service.PermitService;
import cn.seaboot.admin.user.service.RoleService;
import cn.seaboot.admin.user.service.UserDeptService;
import cn.seaboot.commons.core.Asserts;
import cn.seaboot.commons.core.CommonUtils;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;

/**
 * 鉴权配置查询
 * <p>
 * 将用户、角色等相关配置，进行转换，生成易于使用的鉴权信息，这个过程包含对缓存的处理。
 *
 * @author Mr.css
 * @version 2022-01-07 15:39
 */
@Component
public class SecurityAuthorityProvider {

    @Resource
    private UserDao userDao;

    @Resource
    private RoleService roleService;

    @Resource
    private PermitService permitService;

    @Resource
    private UserDeptService userGroupService;

    @Resource
    private SecurityChainService securityChainService;

    /**
     * 缓存类
     */
    @Resource
    private SecurityAntAuthorityCache securityAntAuthorityCache;

    /**
     * 因为security自身的设计原因，需要在角色权限前面加ROLE
     */
    private static final String ROLE_PREFIX = "ROLE_";

    /**
     * 默认添加一个权限，名称为登录，标明必须登录才能访问（个性化设计：只是为了方便组织代码逻辑）
     */
    private static final String ROLE_LOGIN = "ROLE_LOGIN";

    /**
     * 查找白名单 —— PERMIT_ALL
     *
     * @return 符合表达式的权限列表
     */
    @Cacheable(cacheNames = CacheName.SECURITY, key = "'security_chain_authenticated'")
    public Collection<AntAuthority> queryAuthenticatedAnt() {
        List<SecurityChain> list = securityChainService.queryList(AuthorityType.AUTHENTICATED);
        List<AntAuthority> ret = new ArrayList<>();
        for (SecurityChain chain : list) {
            AntAuthority a = new AntAuthority();
            a.setAnt(chain.getAntPath());
            a.setAuthority(null);
            a.setType(AuthorityType.PERMIT_ALL);
            ret.add(a);
        }
        return ret;
    }

    /**
     * 查找允许匿名访问的名单 —— ANONYMOUS
     *
     * @return 符合表达式的权限列表
     */
    @Cacheable(cacheNames = CacheName.SECURITY, key = "'security_chain_anonymous'")
    public Collection<AntAuthority> queryAnonymousAnt() {
        List<SecurityChain> list = securityChainService.queryList(AuthorityType.ANONYMOUS);
        List<AntAuthority> ret = new ArrayList<>();
        for (SecurityChain chain : list) {
            AntAuthority a = new AntAuthority();
            a.setAnt(chain.getAntPath());
            a.setAuthority(null);
            a.setType(AuthorityType.ANONYMOUS);
            ret.add(a);
        }
        return ret;
    }


    /**
     * 查找需要鉴权的名单
     *
     * @return list
     */
    @Cacheable(cacheNames = CacheName.SECURITY, key = "'security_chain_has'")
    public Collection<AntAuthority> queryHasAuthorityAnt() {
        List<SecurityChain> securityChainList = securityChainService.queryList(AuthorityType.HAS_AUTHORITY);
        List<AntAuthority> ret = new ArrayList<>();
        for (SecurityChain chain : securityChainList) {

            String exps = chain.getExpression();
            String[] authorities = exps.split("[,|;]");
            for (int i = 0; i < authorities.length; i++) {
                authorities[i] = authorities[i].trim();
            }

            AntAuthority a = new AntAuthority();
            a.setAnt(chain.getAntPath());
            a.setAuthority(authorities);
            a.setType(AuthorityType.AUTHENTICATED);
            ret.add(a);
        }
        return ret;
    }

    /**
     * 查询某个用户拥有的所有权限
     *
     * @param userName 用户名
     * @return 下发的权限
     * @throws UsernameNotFoundException 用户未找到
     */
    public List<GrantedAuthority> queryGrantedAuthority(String userName) throws UsernameNotFoundException {
        User user = userDao.selectFullByAccount(userName);
        if (user == null) {
            throw new UsernameNotFoundException("User not found!");
        }
        return this.queryGrantedAuthority(user);
    }

    /**
     * 查询某个用户拥有的所有权限
     *
     * @param user 用户
     * @return 下发的权限
     */
    public List<GrantedAuthority> queryGrantedAuthority(User user) {
        UserDept dept = userGroupService.queryById(user.getOrgId(), user.getDeptId());

        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();

        // roles
        grantedAuthorities.addAll(this.queryGrantedRoles(dept));
        // permits
        grantedAuthorities.addAll(this.queryGrantedPermits(dept));
        // default
        grantedAuthorities.add(new SimpleGrantedAuthority(ROLE_LOGIN));
        return grantedAuthorities;
    }


    /**
     * 获取角色列表
     *
     * @param dept 部门
     * @return 角色列表
     */
    public Collection<GrantedAuthority> queryGrantedRoles(UserDept dept) {
        // 包含部门信息，key 值要用部门 ID
        String key = dept.getId();

        Collection<GrantedAuthority> authorities = securityAntAuthorityCache.getRoles(key);
        if (authorities != null) {
            return authorities;
        } else {
            // 用户权限列表
            authorities = new ArrayList<>();
            Role sysRole = roleService.queryById(dept.getOrgId(), dept.getRoleId());
            // 添加用户组权限
            if (CommonUtils.isNotEmpty(dept.getDeptCode())) {
                String role = dept.getDeptCode();
                authorities.add(this.genSimpleGrantedAuthority(role));
            }
            // 添加角色权限
            if (CommonUtils.isNotEmpty(sysRole.getAuthorityCode())) {
                String role = sysRole.getAuthorityCode();
                authorities.add(this.genSimpleGrantedAuthority(role));
            }
            securityAntAuthorityCache.putRoles(key, authorities);
            return authorities;
        }
    }

    /**
     * 获取用户的权限信息
     *
     * @param dept 部门
     * @return 角色列表
     */
    public Collection<GrantedAuthority> queryGrantedPermits(UserDept dept) {
        // 只包含权限信息，key 值用角色 ID 即可
        String key = dept.getRoleId();

        Collection<GrantedAuthority> authorities = securityAntAuthorityCache.getRoles(key);
        if (authorities != null) {
            return authorities;
        } else {
            // 用户权限列表
            authorities = new ArrayList<>();
            // 添加普通权限
            Set<String> perms = permitService.queryPermitAuthority(dept.getRoleId());
            for (String perm : perms) {
                if (CommonUtils.isNotEmpty(perm)) {
                    authorities.add(new SimpleGrantedAuthority(perm));
                }
            }
            securityAntAuthorityCache.putPermits(key, authorities);
            return authorities;
        }
    }

    /**
     * 因为 security 自身的设计原因，分组和权限，以 ROLE 前缀做区分
     *
     * @param role 角色
     * @return SimpleGrantedAuthority
     */
    private SimpleGrantedAuthority genSimpleGrantedAuthority(String role) {
        if (!role.startsWith(ROLE_PREFIX)) {
            role = ROLE_PREFIX + role;
        }
        return new SimpleGrantedAuthority(role);
    }
}
